package com.ideaaedi.commonspring.aop;

import com.ideaaedi.commonds.monitor.WatchTimeUtil;
import com.ideaaedi.commonspring.lite.monitor.WatchTime;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.Ordered;
import org.springframework.util.CollectionUtils;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;

/**
 * 记录耗时
 *
 * @author <font size = "20" color = "#3CAA3C"><a href="https://gitee.com/JustryDeng">JustryDeng</a></font> <img src="https://gitee.com/JustryDeng/shared-files/raw/master/JustryDeng/avatar.jpg" />
 * @since 2100.10.7.LTS17
 */
public class TimeWatcherAdvice implements MethodInterceptor, Ordered {
    
    public static final String BEAN_NAME = "timeWatcherAdvice";
    
    /**
     * 需要包含的methodReference前缀
     * <pre>
     *  注：为空则表示全部包含
     *
     *  注：methodReference形如：com.ideaaedi.commonspring.register.SpringRegister#registerBean
     * </pre>
     */
    private final Set<String> includePrefixes;
    
    /**
     * 需要排除的methodReference前缀
     * <pre>
     *  注：为空则表示都不需要排除
     *
     *  注：methodReference形如：com.ideaaedi.commonspring.register.SpringRegister#registerBean
     * </pre>
     */
    private final Set<String> excludePrefixes;
    
    private final int order;
    
    public TimeWatcherAdvice(Set<String> includePrefixes, Set<String> excludePrefixes, int order) {
        this.includePrefixes = includePrefixes == null ? new HashSet<>() : includePrefixes;
        this.excludePrefixes = excludePrefixes == null ? new HashSet<>() : excludePrefixes;
        this.order = order;
    }
    
    /**
     * 环绕增强
     */
    @Nullable
    @Override
    public Object invoke(@Nonnull MethodInvocation methodInvocation) throws Throwable {
        // 获取目标method
        Method targetMethod = methodInvocation.getMethod();
        // 获取目标Class
        Class<?> targetClazz = targetMethod.getDeclaringClass();
        String clazzName = targetClazz.getCanonicalName();
        if (clazzName == null) {
            clazzName = targetClazz.getName();
        }
        String methodName = targetMethod.getName();
        String methodReference = clazzName + "#" + methodName;
        boolean anyMatchExclude = excludePrefixes.stream().anyMatch(methodReference::startsWith);
        if (anyMatchExclude) {
            return methodInvocation.proceed();
        }
        if (!CollectionUtils.isEmpty(includePrefixes)) {
            boolean anyMatchInclude = includePrefixes.stream().anyMatch(methodReference::startsWith);
            if (!anyMatchInclude) {
                return methodInvocation.proceed();
            }
        }
        // 获取目标annotation
        boolean useDefaultAnnoValue = false;
        WatchTime annotation = targetMethod.getAnnotation(WatchTime.class);
        if (annotation == null) {
            useDefaultAnnoValue = true;
        }
        String clazzSimpleName =  clazzName.substring(clazzName.lastIndexOf(".") + 1);
        Object obj;
        try {
            String taskName;
            if (useDefaultAnnoValue || StringUtils.isBlank(annotation.value())) {
                taskName = clazzSimpleName + "#" + methodName;
            } else {
                taskName = annotation.value();
            }
            WatchTimeUtil.start(taskName);
            obj = methodInvocation.proceed();
        } finally {
            WatchTimeUtil.stop();
        }
        return obj;
    }
    
    @Override
    public int getOrder() {
        return order;
    }
    
}